﻿using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Identity;
using static Volo.Abp.Identity.Settings.IdentitySettingNames;

namespace EcoSystem
{
    public class SystemUserAppService : CrudAppService<
        SystemUser,
        SystemUserDto,
        Guid,
        GetSystemUserInput,
        CreateOrUpdateSystemUserDto,
        CreateOrUpdateSystemUserDto>, ISystemUserAppService
    {

        public IdentityUserManager UserManager { get; set; }

        public IRepository<IdentityRole, Guid> RoleRepository { get; set; }

        public SystemUserAppService(IRepository<SystemUser, Guid> repository) : base(repository)
        {

        }

        public override async Task<SystemUserDto> GetAsync(Guid id)
        {
            var result = await base.GetAsync(id);
            var user = await UserManager.GetByIdAsync(id);
            result.RoleNames = await UserManager.GetRolesAsync(user);
            return result;
        }

        public override async Task<PagedResultDto<SystemUserDto>> GetListAsync(GetSystemUserInput input)
        {
            var query = (await Repository.GetQueryableAsync()).AsNoTracking()
                .WhereIf(input.AccountType.HasValue, x => x.AccountType == input.AccountType)
                .WhereIf(input.EnterpriseId.HasValue, x => x.EnterpriseId == input.EnterpriseId)
                .WhereIf(!input.PhoneNumber.IsNullOrWhiteSpace(), x => x.PhoneNumber.Contains(input.PhoneNumber))
                .WhereIf(!input.Email.IsNullOrWhiteSpace(), x => x.Email.Contains(input.Email))
                .WhereIf(!input.Name.IsNullOrWhiteSpace(), x => x.Name.Contains(input.Name) || x.UserName.Contains(input.Name) || x.UserName.Contains(input.Name));
            var total = await query.CountAsync();
            var items = await query.OrderByDescending(x => x.Id).PageBy(input).ToListAsync();
            return new PagedResultDto<SystemUserDto>()
            {
                Items = await MapToGetListOutputDtosAsync(items),
                TotalCount = total
            };
        }

        public async Task<List<SystemUserDto>> GetAllListAsync(GetSystemUserInput input)
        {
            var query = (await Repository.GetQueryableAsync()).AsNoTracking()
                .WhereIf(input.AccountType.HasValue, x => x.AccountType == input.AccountType)
                .WhereIf(input.EnterpriseId.HasValue, x => x.EnterpriseId == input.EnterpriseId)
                .WhereIf(!input.PhoneNumber.IsNullOrWhiteSpace(), x => x.PhoneNumber.Contains(input.PhoneNumber))
                .WhereIf(!input.Email.IsNullOrWhiteSpace(), x => x.Email.Contains(input.Email))
                .WhereIf(!input.Name.IsNullOrWhiteSpace(), x => x.Name.Contains(input.Name) || x.UserName.Contains(input.Name) || x.UserName.Contains(input.Name));

            var items = await query.OrderByDescending(x => x.Id).ToListAsync();
            var dbContext = await RoleRepository.GetDbContextAsync();
            var userRoleQuery = dbContext.Set<IdentityUserRole>().AsNoTracking();
            var userRoles = await userRoleQuery.AsNoTracking().ToListAsync();
            var roles = await (await RoleRepository.GetQueryableAsync()).AsNoTracking().ToListAsync();
            var resultItems = await MapToGetListOutputDtosAsync(items);
            resultItems.ForEach(item =>
            {
                var roleIds = userRoles.Where(x => x.UserId == item.Id).Select(x => x.RoleId).ToList();
                if (roleIds.Count > 0)
                {
                    item.RoleNames = roles.Where(x => roleIds.Contains(x.Id)).Select(x => x.Name).ToList();
                }
            });
            return resultItems;
        }

        public override async Task<SystemUserDto> CreateAsync(CreateOrUpdateSystemUserDto input)
        {
            input.IsActive = true;
            input.EmailConfirmed = true;
            input.PhoneNumberConfirmed = true;
            var identityUser = new IdentityUser(GuidGenerator.Create(), input.UserName, input.Email);
            identityUser.SetEmailConfirmed(true);
            identityUser.SetPhoneNumber(input.PhoneNumber, true);
            identityUser.SetIsActive(true);
            string password = string.IsNullOrEmpty(input.IntialPassword) ? "1q2w3E*" : input.IntialPassword;
            var result = await UserManager.CreateAsync(identityUser, password);
            if (result.Succeeded)
            {
                if (input.RoleNames?.Count > 0)
                {
                    await UserManager.SetRolesAsync(identityUser, input.RoleNames);
                }
                var entity = await Repository.GetAsync(identityUser.Id);
                ObjectMapper.Map(input, entity);
                await Repository.UpdateAsync(entity);
                return MapToGetOutputDto(entity);
            }
            else
            {
                var error = result.Errors.First();
                throw new UserFriendlyException(error.Description, error.Code, result.ToString());
            }
        }

        public override async Task<SystemUserDto> UpdateAsync(Guid id, CreateOrUpdateSystemUserDto input)
        {
            var entity = await Repository.GetAsync(id);
            MapToEntity(input, entity);
            await Repository.UpdateAsync(entity, true);
            if (input.RoleNames.Count > 0)
            {
                var user = await UserManager.GetByIdAsync(id);
                user.SetEmailConfirmed(true);
                user.SetPhoneNumberConfirmed(true);
                user.SetIsActive(true);
                await UserManager.SetRolesAsync(user, input.RoleNames);
                await UserManager.UpdateAsync(user);
            }
            var result = MapToGetListOutputDto(entity);
            result.RoleNames = input.RoleNames;
            return result;
        }

        public async Task<List<SystemUserDto>> GetUsersForRoleAsync(string roleName)
        {
            var dbContext = await RoleRepository.GetDbContextAsync();
            var roleQuery = (await RoleRepository.GetQueryableAsync()).AsNoTracking();
            var userRoleQuery = dbContext.Set<IdentityUserRole>().AsNoTracking();
            var userIdQuery = from ur in userRoleQuery
                              join role in roleQuery on ur.RoleId equals role.Id
                              where role.Name == roleName
                              select ur.UserId;
            var userIds = await userIdQuery.ToListAsync();

            var query = (await Repository.GetQueryableAsync()).AsNoTracking();
            var items = await query.AsNoTracking().Where(x => userIds.Contains(x.Id)).ToListAsync();
            return await MapToGetListOutputDtosAsync(items);

        }

        public async Task ChangePasswordAsync(ChangePasswordInputDto input)
        {
            var user = await UserManager.GetByIdAsync(CurrentUser.Id.Value);
            if (!await UserManager.CheckPasswordAsync(user, input.OldPassword))
            {
                throw new UserFriendlyException("旧密码输入不正确");
            }
            user.SetEmailConfirmed(true);
            user.SetPhoneNumberConfirmed(true);
            user.SetIsActive(true);
            await UserManager.ChangePasswordAsync(user, input.OldPassword, input.NewPassword);
        }

        public async Task ResetPasswordAsync(ResetPasswordInputDto input)
        {
            var user = await UserManager.GetByIdAsync(input.Id);
            var token = await UserManager.GeneratePasswordResetTokenAsync(user);
            await UserManager.ResetPasswordAsync(user, token, input.Password);
        }
    }
}
